/*
 * Copyright (c) 2009, 2013, Oracle and/or its affiliates. All rights reserved.
 * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 */

package java.nio.file;

import java.util.Set;
import java.util.EnumSet;
import java.security.SecureRandom;

import static java.security.AccessController.*;

import java.io.IOException;
import java.nio.file.attribute.FileAttribute;
import java.nio.file.attribute.PosixFilePermission;
import java.nio.file.attribute.PosixFilePermissions;

import static java.nio.file.attribute.PosixFilePermission.*;

import sun.security.action.GetPropertyAction;


/**
 * Helper class to support creation of temporary files and directories with
 * initial attributes.
 */

class TempFileHelper {

  private TempFileHelper() {
  }

  // temporary directory location
  private static final Path tmpdir =
      Paths.get(doPrivileged(new GetPropertyAction("java.io.tmpdir")));

  private static final boolean isPosix =
      FileSystems.getDefault().supportedFileAttributeViews().contains("posix");

  // file name generation, same as java.io.File for now
  private static final SecureRandom random = new SecureRandom();

  private static Path generatePath(String prefix, String suffix, Path dir) {
    long n = random.nextLong();
    n = (n == Long.MIN_VALUE) ? 0 : Math.abs(n);
    Path name = dir.getFileSystem().getPath(prefix + Long.toString(n) + suffix);
    // the generated name should be a simple file name
    if (name.getParent() != null) {
      throw new IllegalArgumentException("Invalid prefix or suffix");
    }
    return dir.resolve(name);
  }

  // default file and directory permissions (lazily initialized)
  private static class PosixPermissions {

    static final FileAttribute<Set<PosixFilePermission>> filePermissions =
        PosixFilePermissions.asFileAttribute(EnumSet.of(OWNER_READ, OWNER_WRITE));
    static final FileAttribute<Set<PosixFilePermission>> dirPermissions =
        PosixFilePermissions.asFileAttribute(EnumSet
            .of(OWNER_READ, OWNER_WRITE, OWNER_EXECUTE));
  }

  /**
   * Creates a file or directory in in the given given directory (or in the
   * temporary directory if dir is {@code null}).
   */
  private static Path create(Path dir,
      String prefix,
      String suffix,
      boolean createDirectory,
      FileAttribute<?>[] attrs)
      throws IOException {
    if (prefix == null) {
      prefix = "";
    }
    if (suffix == null) {
      suffix = (createDirectory) ? "" : ".tmp";
    }
    if (dir == null) {
      dir = tmpdir;
    }

    // in POSIX environments use default file and directory permissions
    // if initial permissions not given by caller.
    if (isPosix && (dir.getFileSystem() == FileSystems.getDefault())) {
      if (attrs.length == 0) {
        // no attributes so use default permissions
        attrs = new FileAttribute<?>[1];
        attrs[0] = (createDirectory) ? PosixPermissions.dirPermissions :
            PosixPermissions.filePermissions;
      } else {
        // check if posix permissions given; if not use default
        boolean hasPermissions = false;
        for (int i = 0; i < attrs.length; i++) {
          if (attrs[i].name().equals("posix:permissions")) {
            hasPermissions = true;
            break;
          }
        }
        if (!hasPermissions) {
          FileAttribute<?>[] copy = new FileAttribute<?>[attrs.length + 1];
          System.arraycopy(attrs, 0, copy, 0, attrs.length);
          attrs = copy;
          attrs[attrs.length - 1] = (createDirectory) ?
              PosixPermissions.dirPermissions :
              PosixPermissions.filePermissions;
        }
      }
    }

    // loop generating random names until file or directory can be created
    SecurityManager sm = System.getSecurityManager();
    for (; ; ) {
      Path f;
      try {
        f = generatePath(prefix, suffix, dir);
      } catch (InvalidPathException e) {
        // don't reveal temporary directory location
        if (sm != null) {
          throw new IllegalArgumentException("Invalid prefix or suffix");
        }
        throw e;
      }
      try {
        if (createDirectory) {
          return Files.createDirectory(f, attrs);
        } else {
          return Files.createFile(f, attrs);
        }
      } catch (SecurityException e) {
        // don't reveal temporary directory location
        if (dir == tmpdir && sm != null) {
          throw new SecurityException("Unable to create temporary file or directory");
        }
        throw e;
      } catch (FileAlreadyExistsException e) {
        // ignore
      }
    }
  }

  /**
   * Creates a temporary file in the given directory, or in in the
   * temporary directory if dir is {@code null}.
   */
  static Path createTempFile(Path dir,
      String prefix,
      String suffix,
      FileAttribute<?>[] attrs)
      throws IOException {
    return create(dir, prefix, suffix, false, attrs);
  }

  /**
   * Creates a temporary directory in the given directory, or in in the
   * temporary directory if dir is {@code null}.
   */
  static Path createTempDirectory(Path dir,
      String prefix,
      FileAttribute<?>[] attrs)
      throws IOException {
    return create(dir, prefix, null, true, attrs);
  }
}
